package com.cgs.mongo.engine;

import com.cgs.mongo.delegate.MongoCollectionWrapper;
import com.cgs.mongo.exception.ParserException;
import com.mongodb.client.MongoCollection;
import jdk.nashorn.api.scripting.ClassFilter;
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
import org.bson.Document;

import javax.script.ScriptException;
import javax.script.SimpleBindings;
import java.util.concurrent.*;

/**
 * 脚本执行引擎
 */
public enum ScriptEngine {

    INSTANCE;

    /**
     * Nashorn实例
     */
    public final javax.script.ScriptEngine ENGINE = new NashornScriptEngineFactory().getScriptEngine(new String[]{"-doe", "--global-per-engine"}
            , Thread.currentThread().getContextClassLoader(), new JavaClassFilter());

    /**
     * 线程实例
     */
    private final ExecutorService executorService = ThreadManager.getDaemonExecutor("STRING.MONGO.QUERY");

    public Object run(MongoCollection<Document> collection, String script, long timeout) {
        return getResult(timeout, executorService.submit(() -> {
            try {
                SimpleBindings simpleBindings = new SimpleBindings();
                simpleBindings.put("col", new MongoCollectionWrapper(collection));
                return ENGINE.eval(script, simpleBindings);
            } catch (ScriptException e) {
                e.printStackTrace();
                return null;
            }
        }));
    }

    public Object run(OperationalContext operationalContext) {
        return run(operationalContext.getCollection(), operationalContext.getScript(), operationalContext.getTimeout());
    }

    public static <T> T getResult(long timeout, Future<T> future) {
        try {
            return future.get(timeout > 0L ? timeout : 10L, TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            future.cancel(true);
            throw new ParserException("脚本可能存在死循环或线程等待！", e);
        }catch (Exception e) {
            throw new ParserException("脚本执行出错！", e);
        }
    }

    /**
     * 过滤System类
     */
    static class JavaClassFilter implements ClassFilter {
        @Override
        public boolean exposeToScripts(String s) {
            return !s.startsWith("java.lang.System");
        }
    }
}
